﻿/*
 * Twipler - Twitter Web Client with features.
 *  
 *  Copyright (C) 2009, 2010. Ian Quigley
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details. 
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web;
using System.IO;
using Newtonsoft.Json;

namespace Twipler.App.DataDoc
{
    public class DataDocList : IDataDoc
    {
        protected List<DataDocValue> Document;
        protected DataDocStatus _status = DataDocStatus.Ok;

        public DataDocList()
        { }

        public DataDocList(List<DataDocValue> document)
        {
            Document = document;
        }

        public DataDocList(List<DataDocValue> document, DataDocStatus status)
        {
            Document = document;
            _status = status;
        }

        public DataDocList(DataDocStatus status, string message)
        {
            Document = new List<DataDocValue>() { new DataDocValue("message", message) };
            _status = status;
        }

        public void OrderBy(Func<DataDocValue, DateTime> keySelector)
        {
            Document.OrderBy(keySelector);
        }

        public DataDocStatus Status { get { return _status; } }

        public void Add(DataDocValue dataDocValue)
        {
            if (Document == null)
                Document = new List<DataDocValue>();

            Document.Add(dataDocValue);
        }

        public void Add(string name, object value)
        {
            Add(new DataDocValue(name, value));
        }

		public void Insert(string injectAtPath, DataDocValue item)
		{
			if (injectAtPath == string.Empty)
				return;

			DataDocValue v = FindValue(injectAtPath);
			if (v == null)
				return;

			if (v.Value is List<DataDocValue>)
				((List<DataDocValue>) v.Value).Add(item);
		}

        public DataDocValue Find(string key)
        {
            return FindValue(key);
        }

        private DataDocValue FindValue(string key)
        {
            string[] path;
            if (key == "/")
                path = new[] { string.Empty };
            else
                path = key.Split('/');

            List<DataDocValue> from = Document;
            for (int i = 0; i < path.Length; i++)
            {
                DataDocValue val = FindFirst(from, path[i]);
                if (val == null)
                    return null;

                if (((i + 1) == path.Length))
                    return val;

                // not a sub list, so break
                if (!(val.Value is List<DataDocValue>))
                    return null;

                from = (List<DataDocValue>)val.Value;

            }

            return from.FirstOrDefault();
        }

        private DataDocValue FindFirst(List<DataDocValue> from, string key)
        {
            return from.FirstOrDefault(val => val.Name == key);
        }

        public List<IDataDoc> Select(string path)
        {
            if (path == string.Empty)
                return new List<IDataDoc> { new DataDocList(Document) };
            
            DataDocValue v = FindValue(path);
            if (v == null)
                return null;

            if (v.Value is List<DataDocValue>)
            {
                List<IDataDoc> docs = new List<IDataDoc>();

                foreach (DataDocValue va in ((List<DataDocValue>)v.Value))
                {
                    if (va.Value is List<DataDocValue>)
                        docs.Add(new DataDocList((List<DataDocValue>)va.Value));
                    else
                        docs.Add(new DataDocList(new List<DataDocValue>() { va }));
                }

                return docs;
            }
            
            return new List<IDataDoc> { new DataDocList(new List<DataDocValue>() { v }) };
        }

        public void IsSucessSetMessage(string message)
        {
            if (!string.IsNullOrEmpty(message) && (_status == DataDocStatus.Ok) && (Find("message") == null))
                Add("message", message);
        }

		public override string ToString()
		{
			StringBuilder output = new StringBuilder();
			output.Append("{");

			foreach (DataDocValue v in Document)
				output.AppendFormat(" [  {0}  ]" + Environment.NewLine, v);

			output.Append("}");

			return output.ToString();
		}

        public void DebugRender(StringBuilder html, int indent)
        {
            foreach (DataDocValue v in Document)
            {
                v.DebugRender(html, indent);
            }
        }

        public void SortBy(string key, Func<object, object, int> compare)
        {
            if ((Document.Count() != 1) || !(Document[0].Value is List<DataDocValue>))
                return;

            ((List<DataDocValue>)Document[0].Value)
                .Sort(new CompareDataDocValue(compare, key));
        }

        private class CompareDataDocValue : Comparer<DataDocValue>
        {
            private string _key;
            private Func<object, object, int> _compare;

            public CompareDataDocValue(Func<object, object, int> compare, string key)
            {
                _compare = compare;
                _key = key;
            }

            public override int Compare(DataDocValue x, DataDocValue y)
            {
                return _compare(((List<DataDocValue>)x.Value).First( t => t.Name == _key).Value,
                                ((List<DataDocValue>)y.Value).First( t => t.Name == _key).Value
                                );
            }
        }
    }
}